﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

namespace GameOfLife
{
    public partial class Form1 : Form
    {
        private static int[][] board;
        private const int SIZE = 32;
        private const int CELL_SIDE_LEN = 20;
        private static int alive = 0;
        private static int generation = 0;
        private Dictionary<int, Color> colors = new Dictionary<int, Color>();
        private bool isRunning = false;

        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams paras = base.CreateParams;
                paras.ExStyle |= 0x02000000;
                return paras;
            }
        }

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            InitializeGame();
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            LifeUpdate();
            GamgOverDetected();
        }

        void InitializeGame()
        {
            // initialize colors
            colors[0] = Color.Gainsboro;
            colors[1] = Color.MediumSeaGreen;

            // initialize board
            Random ran = new Random();
            board = new int[SIZE][];
            for (int i = 0; i < SIZE; i++)
            {
                board[i] = new int[SIZE];
                for (int j = 0; j < SIZE; j++)
                {
                    if (i == 0)
                    {
                        // initialize grid
                        this.dgvBoard.Columns.Add(null, null);
                        this.dgvBoard.Rows.Add();
                    }

                    int status = ran.Next(2);
                    board[i][j] = status;

                    if (status == 1)
                        alive++;

                    this.dgvBoard.Rows[i].Cells[j].Style.BackColor = colors[board[i][j]];
                }
            }

            this.dgvBoard.ClearSelection();

            // resize grid cell side size
            for (int i = 0; i < dgvBoard.Rows.Count; dgvBoard.Rows[i++].Height = CELL_SIDE_LEN) ;
            for (int i = 0; i < dgvBoard.Columns.Count; dgvBoard.Columns[i++].Width = CELL_SIDE_LEN) ;

            this.lblAlive.Text = alive.ToString();

            generation++;
            this.lblGeneration.Text = generation.ToString();

            this.lblDead.Text = (SIZE * SIZE - alive).ToString();
        }

        void LifeUpdate(bool isRestart = false)
        {
            if (isRestart)
            {
                Random ran = new Random();
                board = new int[SIZE][];
                for (int i = 0; i < SIZE; i++)
                {
                    board[i] = new int[SIZE];
                    for (int j = 0; j < SIZE; j++)
                    {
                        int status = ran.Next(2);
                        board[i][j] = status;
                    }
                }
            }

            #region board update                

            List<bool> flag = new List<bool>();

            if (board.Length == 1) // 1 row
            {
                for (int i = 0; i < board[0].Length; i++)
                {
                    if (i == 0 || i == board[0].Length - 1)
                    {
                        flag.Add(board[0][i] == 1);
                    }
                    else
                    {
                        int currState = board[0][i];
                        if (currState <= 0)
                            flag.Add(false);
                        else
                        {
                            flag.Add(board[0][i - 1] == 0 || board[0][i + 1] == 0);
                        }
                    }
                }
            }
            else if (board[0].Length == 1) // 1 column
            {
                for (int i = 0; i < board.Length; i++)
                {
                    if (i == 0 || i == board.Length - 1)
                    {
                        flag.Add(board[i][0] == 1);
                    }
                    else
                    {
                        int currState = board[i][0];
                        if (currState <= 0)
                            flag.Add(false);
                        else
                        {
                            flag.Add(board[i - 1][0] == 0 || board[i + 1][0] == 0);
                        }
                    }
                }
            }
            else
            {
                for (int i = 0; i < board.Length; i++)
                {
                    for (int j = 0; j < board[i].Length; j++)
                    {
                        int currState = board[i][j];

                        if (i == 0) // top
                        {
                            if (j == 0)
                            {
                                if (currState <= 0)
                                {
                                    flag.Add((j < board[i].Length - 1 && board[i][j + 1] == 1) && board[i + 1][j] == 1 && board[i + 1][j + 1] == 1);
                                }
                                else
                                {
                                    int liveCount = 0;
                                    if (board[i][j + 1] == 1) liveCount++;
                                    if (board[i + 1][j] == 1) liveCount++;
                                    if (board[i + 1][j + 1] == 1) liveCount++;
                                    flag.Add(liveCount < 2);
                                }
                            }
                            else if (j == board[i].Length - 1)
                            {
                                if (currState <= 0)
                                {
                                    flag.Add(board[i][j - 1] == 1 && board[i + 1][j] == 1 && board[i + 1][j - 1] == 1);
                                }
                                else
                                {
                                    int liveCount = 0;
                                    if (board[i][j - 1] == 1) liveCount++;
                                    if (board[i + 1][j] == 1) liveCount++;
                                    if (board[i + 1][j - 1] == 1) liveCount++;
                                    flag.Add(liveCount < 2);
                                }
                            }
                            else
                            {
                                int liveCount = 0;
                                if (board[i][j - 1] == 1) liveCount++;
                                if (board[i + 1][j - 1] == 1) liveCount++;
                                if (board[i + 1][j] == 1) liveCount++;
                                if (board[i + 1][j + 1] == 1) liveCount++;
                                if (board[i][j + 1] == 1) liveCount++;
                                if (currState <= 0)
                                {
                                    flag.Add(liveCount == 3);
                                }
                                else
                                {
                                    flag.Add(liveCount < 2 || liveCount > 3);
                                }
                            }
                        }
                        else if (i > 0 && i < board.Length - 1 && j == 0) // left
                        {
                            int liveCount = 0;
                            if (board[i - 1][j] == 1) liveCount++;
                            if (board[i - 1][j + 1] == 1) liveCount++;
                            if (board[i][j + 1] == 1) liveCount++;
                            if (board[i + 1][j + 1] == 1) liveCount++;
                            if (board[i + 1][j] == 1) liveCount++;
                            if (currState <= 0)
                            {
                                flag.Add(liveCount == 3);
                            }
                            else
                            {
                                flag.Add(liveCount < 2 || liveCount > 3);
                            }
                        }
                        else if (i > 0 && i < board.Length - 1 && j == board[i].Length - 1) // right
                        {
                            int liveCount = 0;
                            if (board[i - 1][j] == 1) liveCount++;
                            if (board[i - 1][j - 1] == 1) liveCount++;
                            if (board[i][j - 1] == 1) liveCount++;
                            if (board[i + 1][j - 1] == 1) liveCount++;
                            if (board[i + 1][j] == 1) liveCount++;
                            if (currState <= 0)
                            {
                                flag.Add(liveCount == 3);
                            }
                            else
                            {
                                flag.Add(liveCount < 2 || liveCount > 3);
                            }
                        }
                        else if (i == board.Length - 1) // bottom
                        {
                            if (j == 0)
                            {
                                if (currState <= 0)
                                {
                                    flag.Add(board[i][j + 1] == 1 && board[i - 1][j] == 1 && board[i - 1][j + 1] == 1);
                                }
                                else
                                {
                                    int liveCount = 0;
                                    if (board[i][j + 1] == 1) liveCount++;
                                    if (board[i - 1][j] == 1) liveCount++;
                                    if (board[i - 1][j + 1] == 1) liveCount++;
                                    flag.Add(liveCount < 2);
                                }
                            }
                            else if (j == board[i].Length - 1)
                            {
                                if (currState <= 0)
                                {
                                    flag.Add(board[i][j - 1] == 1 && board[i - 1][j] == 1 && board[i - 1][j - 1] == 1);
                                }
                                else
                                {
                                    int liveCount = 0;
                                    if (board[i][j - 1] == 1) liveCount++;
                                    if (board[i - 1][j] == 1) liveCount++;
                                    if (board[i - 1][j - 1] == 1) liveCount++;
                                    flag.Add(liveCount < 2);
                                }
                            }
                            else
                            {
                                int liveCount = 0;
                                if (board[i][j - 1] == 1) liveCount++;
                                if (board[i - 1][j - 1] == 1) liveCount++;
                                if (board[i - 1][j] == 1) liveCount++;
                                if (board[i - 1][j + 1] == 1) liveCount++;
                                if (board[i][j + 1] == 1) liveCount++;
                                if (currState <= 0)
                                {
                                    flag.Add(liveCount == 3);
                                }
                                else
                                {
                                    flag.Add(liveCount < 2 || liveCount > 3);
                                }
                            }
                        }
                        else // middle
                        {
                            int liveCount = 0;
                            if (board[i - 1][j - 1] == 1) liveCount++;
                            if (board[i - 1][j] == 1) liveCount++;
                            if (board[i - 1][j + 1] == 1) liveCount++;
                            if (board[i][j - 1] == 1) liveCount++;
                            if (board[i][j + 1] == 1) liveCount++;
                            if (board[i + 1][j - 1] == 1) liveCount++;
                            if (board[i + 1][j] == 1) liveCount++;
                            if (board[i + 1][j + 1] == 1) liveCount++;
                            if (currState <= 0)
                            {
                                flag.Add(liveCount == 3);
                            }
                            else
                            {
                                flag.Add(liveCount < 2 || liveCount > 3);
                            }
                        }
                    }
                }
            }

            #endregion

            #region game info update

            alive = 0;

            for (int i = 0; i < board.Length; i++)
            {
                for (int j = 0; j < board[i].Length; j++)
                {
                    if (flag[i * board[i].Length + j])
                        board[i][j] ^= 1;

                    if (board[i][j] == 1)
                        alive++;

                    this.dgvBoard.Rows[i].Cells[j].Style.BackColor = colors[board[i][j]];
                }
            }

            this.lblAlive.Text = alive.ToString();

            if (!isRestart)
            {
                generation++;
            }
            else
            {
                generation = 1;
            }
            this.lblGeneration.Text = generation.ToString();

            this.lblDead.Text = (SIZE * SIZE - alive).ToString();

            #endregion
        }

        private void GamgOverDetected()
        {
            if (alive == 0)
            {
                this.timer1.Stop();
                MessageBox.Show("GAME OVER");
            }
        }

        private void btnStartPause_Click(object sender, EventArgs e)
        {
            if (isRunning)
            {
                this.timer1.Stop();
                this.btnStartPause.Image = global::GameOfLife.Properties.Resources.start;
                this.btnRestart.Visible = true;
            }
            else
            {
                this.timer1.Start();
                this.btnStartPause.Image = global::GameOfLife.Properties.Resources.pause;
                this.btnRestart.Visible = false;
            }

            isRunning ^= true;
        }

        private void btnRestart_Click(object sender, EventArgs e)
        {
            LifeUpdate(isRestart: true);
        }

        private void dgvBoard_CellClick(object sender, DataGridViewCellEventArgs e)
        {
            //if (!isRunning)
            //{
            //    board[e.RowIndex][e.ColumnIndex] ^= 1;
            //    this.dgvBoard.Rows[e.RowIndex].Cells[e.ColumnIndex].Style.BackColor = colors[board[e.RowIndex][e.ColumnIndex]];
            //}
        }
    }
}
